<?php

namespace services;

use exceptions\PayException;
use utils\Hook;
use think\facade\Log;
use think\facade\Route as Url;
use Alipay\EasySDK\Kernel\Config;
use Alipay\EasySDK\Kernel\Factory;
use app\services\pay\PayNotifyServices;
use Alipay\EasySDK\Kernel\Util\ResponseChecker;

/**
 * Class AliPayService
 * @package services
 */
class AliPayService
{

    /**
     * 配置
     * @var array
     */
    protected $config = [
        'appId'              => '',
        'merchantPrivateKey' => '',//应用私钥
        'alipayPublicKey'    => '',//支付宝公钥
        'notifyUrl'          => '',//可设置异步通知接收服务地址
        'encryptKey'         => '',//可设置AES密钥，调用AES加解密相关接口时需要（可选）
    ];

    /**
     * @var ResponseChecker
     */
    protected $response;

    /**
     * @var static
     */
    protected static $instance;

    /**
     * AliPayService constructor.
     *
     * @param array $config
     */
    protected function __construct(array $config = [])
    {
        if (!$config) {
            $config = [
                'appId'              => sysconfig('ali_pay_appid'),
                'merchantPrivateKey' => sysconfig('alipay_merchant_private_key'),
                'alipayPublicKey'    => sysconfig('alipay_public_key'),
                'notifyUrl'          => sysconfig('site_url') . Url::buildUrl('/api/pay/notify/alipay'),
            ];
        }
        $this->config = array_merge($this->config, $config);
        $this->initialize();
        $this->response = new ResponseChecker();
    }

    /**
     * 实例化
     *
     * @param array $config
     *
     * @return static
     */
    public static function instance(array $config = [])
    {
        if (is_null(self::$instance)) {
            self::$instance = new static($config);
        }
        return self::$instance;
    }

    /**
     * 初始化
     */
    protected function initialize()
    {
        Factory::setOptions($this->getOptions());
    }

    /**
     * 设置配置
     * @return Config
     */
    protected function getOptions()
    {
        $options              = new Config();
        $options->protocol    = 'https';
        $options->gatewayHost = 'openapi.alipay.com';
        $options->signType    = 'RSA2';

        $options->appId = $this->config['appId'];
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        $options->merchantPrivateKey = 'MIIEowIBAAKCAQEAhHrYHdzyvJ+Ql+GSuKG93lpvlVnxaGSmY5J0O1GuTVGuGTbu6PMXDXnqU5j34vMbgkSu5jPQ13EEByRu0gjQTOzW/ztstqYklBuvc0lYJZHSKwG7gTZk5kEQXCZr2dT67Dtyv7hzYsFuzRLz9simRNkr1qmUgsyz8BlyEMGQBb96UzAmL7m9i7CfRq8NqUuP7WiVKH7fAKQPHOHtf32HSc66QKI8Ff1AT57YCO19Amo49o92FZg/AoyfEjEEPgeotpoWC15xrDhJoTABJrVoJfsWq2e1LhuvYoqedqMt6XWYB1kKrR9QCO4EYzmJjfgEwGlSsepciioEcRLKvoxjBwIDAQABAoIBACcTHJ9iRe5AmiNSEzaF7jyhK/w/NSb3WORRlBrS8rg3eURSoZDv8kK1TNovah30Wo7Ht1aKvfRtjySUKGOm2dl2mctaOax9wJg2uj8kyOz736NztVxdMNGBK1QiuPjdCs2WkzfdLZtZNlciWfbWI8R20alVB0q1GfWda7lpfhNThA1aWnEelU9ie/U9tJDM2wBdL9P1RKb+T9DSKVK28a34ViPhMS45WQjSVNvhFUkOT9A56ndrJOC94AeIu+cWqIZ+6ntdgEh+NQlbG5n5z3yGBjOcEhVYFcXfClgPE0LrsGmAxJedY8ZPPgNkagEvf9AdG85GY2WiNU0oGXDqHMECgYEAvjn1YV61HbfSnpSyu/JVb4Xh0VwiR0jnfxnNAT93dWROk1AdTrBKPqlmFDbQAg39W22bPL6aXnXLOscgqi2fG9+GrvTufTZTRZ69isyrLnzw/yQf4mPRbguMLmDnA/D0j8OlMDfGM1NGdfUxH4V3XQ+PoRj7d9+sIQPKiLpFjRcCgYEAsklnlozTfBH0yl+Wh+10GllhNAlSLM+4qH9wpYRv2uWNqMBEp7C7GEsxdWpddU+nJwe51jThlbK6cR7zzlG+xYH6uuPLNVrD3BlUi6IX35KO48btDi66Tu2rZa/zsL0AO7arHHx5MkY1eJ9R83RrIlUPQI7CbOPXj+Tu9hBp75ECgYBODyCHRULTIBSYqHIzt7yhV/ITZFhYoi0EtgSu52lQzcmEn9J28NZ2ojLWRqvSvATkWLTkF17nNjUgOIhJ7RCmcnIlJzMaRImlPv+OjnAMUo7eU+eVkdpl6u0qNu91aZIh0GnfwHNfqmN2khgHVFBn6rPIqqS+SxWqMpEQzEcwHwKBgQCDoGAjLWuCATvKIZJgXnZGcdCq+2q4/M8nT5daDBIM+UeB3BmT+I3ZHMKyHkpLrvgIf9K7DCW0QNUXaTO1EnMXHKSBUGaZyJ4rpGPgB1c6z9hA4H/p5aPVW/EbzTDMPm2qqG1iRZ+w91bI7E8zv7LNESGGlx+CByEB8pJeBrY/kQKBgDnlpEJz3jCmyyPMt1K85LCdbWg2cj58knty7kE2ujvoN6dYuuSVaKuItWAs7zwyV4pWswTrr9Rut1iUDnD9Lm7l0X29C02jR61DNuvGjrymfdvuL8LOO6RIG++Qj7JYWcOK9K8A4xDEAr4B3dfCt11KIcx0moi9N82aue7fn0TL';//$this->config['merchantPrivateKey'];
        //注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        $options->alipayPublicKey = 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnVRZ3eYRRxgnKKAYFro61FSMOx+N+AndC2TO3ezQ7D/6SLwiOPn47Ps1UUkscXDVdm87jtOYCGB7DUesHyXxN1Cy0wr1q2g/ByUCQV9MO4bQDOGF4ph0RgbEQEnYYo0DuNuYhBHm37osZgLx0DIQMQ1ptuwmxy0rw5ejzTfgeky7iW3hhg39pNij0CIczltD/mdlvuSsT8wLMfxgfra10cQeOLgM4fcEeX4Y5STpMq9zEecnr6ypRPQk5h32Hvcm8bvFbKVjOaiswaftVLHyvjLDaU+MByZYUTBMcjly6kIcVY5EEwJc5Aan5zwJwwsI5jm0EJokBozFn9BH2uz2vwIDAQAB';//$this->config['alipayPublicKey'];
        //可设置异步通知接收服务地址（可选）
        $options->notifyUrl = $this->config['notifyUrl'];
        //可设置AES密钥，调用AES加解密相关接口时需要（可选）
        if ($this->config['encryptKey']) {
            $options->encryptKey = $this->config['encryptKey'];
        }

        return $options;
    }

    /**
     * Description: 手机网站支付.创建订单
     *
     * @param string        $title          商品名称
     * @param string        $orderId        订单号
     * @param string        $totalAmount    支付金额
     * @param string|object $passbackParams 备注
     * @param string        $quitUrl        同步跳转地址
     *
     * @return \Alipay\EasySDK\Payment\FaceToFace\Models\AlipayTradePrecreateResponse|\Alipay\EasySDK\Payment\Wap\Models\AlipayTradeWapPayResponse|string
     */
    public function create(string $title, string $orderId, string $totalAmount, $passbackParams, string $quitUrl = '', string $siteUrl = '', bool $isCode = false)
    {
        try {
            if ($isCode) {
                $result = Factory::payment()->faceToFace()->optional('passback_params', $passbackParams)->preCreate($title, $orderId, $totalAmount);
            } else {
                $result = Factory::payment()->wap()->optional('passback_params', $passbackParams)->pay($title, $orderId, $totalAmount, $quitUrl, $siteUrl);
            }
            if ($this->response->success($result)) {
                return isset($result->body) ? $result->body : $result;
            } else {
                throw new PayException('失败原因:' . $result->msg . ',' . $result->subMsg);
            }
        } catch (\Exception $e) {
            throw new PayException($e->getMessage());
        }
    }

    /**
     * 订单退款
     *
     * @param string $outTradeNo  订单号
     * @param string $totalAmount 金额
     *
     * @return \Alipay\EasySDK\Payment\Common\Models\AlipayTradeRefundResponse
     */
    public function refund(string $outTradeNo, string $totalAmount)
    {
        try {
            $result = Factory::payment()->common()->refund($outTradeNo, $totalAmount);
            if ($this->response->success($result)) {
                return $result;
            } else {
                throw new PayException('失败原因:' . $result->msg . ',' . $result->subMsg);
            }
        } catch (\Exception $e) {
            throw new PayException($e->getMessage());
        }
    }

    /**
     * 查询交易退款单号信息
     *
     * @param string $outTradeNo
     * @param string $outRequestNo
     *
     * @return \Alipay\EasySDK\Payment\Common\Models\AlipayTradeFastpayRefundQueryResponse
     */
    public function queryRefund(string $outTradeNo, string $outRequestNo)
    {
        try {
            $result = Factory::payment()->common()->queryRefund($outTradeNo, $outRequestNo);
            if ($this->response->success($result)) {
                return $result;
            } else {
                throw new PayException('失败原因:' . $result->msg . ',' . $result->subMsg);
            }
        } catch (\Exception $e) {
            throw new PayException($e->getMessage());
        }
    }

    /**
     * 支付异步回调
     * @return string
     */
    public static function handleNotify()
    {
        return self::instance()->notify(function ($notify) {
            if (isset($notify->out_trade_no)) {
                if (isset($notify->attach) && $notify->attach) {
                    if (($count = strpos($notify->out_trade_no, '_')) !== false) {
                        $notify->trade_no     = $notify->out_trade_no;
                        $notify->out_trade_no = substr($notify->out_trade_no, $count + 1);
                    }
                    return (new Hook(PayNotifyServices::class, 'aliyun'))->listen($notify->attach, $notify->out_trade_no, $notify->trade_no);
                }
                return false;
            }
        });
    }

    /**
     * 异步回调
     *
     * @param callable $notifyFn
     *
     * @return string
     */
    public function notify(callable $notifyFn)
    {
        app()->request->filter(['trim']);
        $paramInfo = app()->request->param();
        if (isset($paramInfo['type'])) {
            unset($paramInfo['type']);
        }
        //商户订单号
        $postOrder['out_trade_no'] = $paramInfo['out_trade_no'] ?? '';
        //支付宝交易号
        $postOrder['trade_no'] = $paramInfo['trade_no'] ?? '';
        //交易状态
        $postOrder['trade_status'] = $paramInfo['trade_status'] ?? '';
        //备注
        $postOrder['attach'] = isset($paramInfo['passback_params']) ? urldecode($paramInfo['passback_params']) : '';
        if ($this->verifyNotify($paramInfo)) {
            try {
                if ($notifyFn((object)$postOrder)) {
                    return 'success';
                }
            } catch (\Exception $e) {
                Log::error('支付宝异步会回调成功,执行函数错误。错误单号：' . $postOrder['out_trade_no']);
            }
        }
        return 'fail';

    }

    /**
     * 验签
     * @return bool
     */
    protected function verifyNotify(array $param)
    {
        try {
            return Factory::payment()->common()->verifyNotify($param);
        } catch (\Exception $e) {
            Log::error('支付宝回调成功,验签发生错误，错误原因:' . $e->getMessage());
        }
        return false;
    }
}
